/*
 *  Arrow
 *
 *  Created by David Giovannini on 1/5/07.
 *  Copyright 2007 Software by Jove. All rights reserved.
 *
 */
 
#include "Blocker.h"

#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/xtime.hpp>

#include <math.h>

namespace
{

struct isFinished
{
	isFinished( const size_t& completion, size_t& finished )
		: m_completion( completion ), m_finished( finished ) {}
	bool operator () ( void ) const
		{ return m_finished >= m_completion; }
	const size_t& m_completion;
	const size_t& m_finished;
};

}

namespace SBJ
{

class Blocker::Pimpl
{
public:
	Pimpl( const size_t completion )
	: m_useCount( 1 ), m_completion( completion ), m_finished( 0 )
	{
	}

	int m_useCount;
	boost::mutex m_guard;
	boost::condition m_condition;

	const size_t m_completion;
	size_t m_finished;
};

Blocker::Blocker( const size_t completion )
: m_pimpl( completion > 0 ? new Pimpl( completion ) : 0 )
{
}

Blocker::Blocker( const Blocker& rhs )
: m_pimpl( 0 )
{
	use( rhs.m_pimpl );
}

Blocker::~Blocker( void )
{
	unusePimpl();
}

Blocker&
Blocker::operator = ( const Blocker& rhs )
{
	unusePimpl();
	use( rhs.m_pimpl );
	return *this;
}

void
Blocker::swap( Blocker& rhs )
{
	Pimpl* temp( m_pimpl );
	m_pimpl = rhs.m_pimpl;
	rhs.m_pimpl = temp;
}

void
Blocker::use( Pimpl* pimpl )
{
	m_pimpl = pimpl;
	if ( m_pimpl != 0 )
	{
		boost::mutex::scoped_lock lock( m_pimpl->m_guard );
		++m_pimpl->m_useCount;
	}
}

void
Blocker::unusePimpl( void )
{
	if ( m_pimpl != 0 )
	{
		int localCount = 0;
		{
			boost::mutex::scoped_lock lock( m_pimpl->m_guard );
			--m_pimpl->m_useCount;
			localCount = m_pimpl->m_useCount;
		}
		if ( localCount == 0 )
		{
			delete m_pimpl;
		}
	}
}

void
Blocker::wait( void )
{
	if ( m_pimpl != 0 )
	{
		boost::mutex::scoped_lock lock( m_pimpl->m_guard );
		m_pimpl->m_condition.wait( lock, isFinished( m_pimpl->m_completion, m_pimpl->m_finished ) );
	}
}

bool
Blocker::timed_wait( double seconds )
{
	if ( m_pimpl != 0 )
	{
		boost::xtime cur;
		boost::xtime_get(&cur, boost::TIME_UTC);
		boost::xtime time =
		{
			cur.sec + (int)::floor( seconds ),
			cur.nsec + (int)( ( seconds - ::floor( seconds ) ) * 1000000000.0 )
		};

		boost::mutex::scoped_lock lock( m_pimpl->m_guard );
		return m_pimpl->m_condition.timed_wait( lock, time, isFinished( m_pimpl->m_completion, m_pimpl->m_finished ) );
	}
	return true; // When there is nothing to finish, we are finished.
}

void
Blocker::finish( void )
{
	if ( m_pimpl != 0 )
	{
		boost::mutex::scoped_lock lock( m_pimpl->m_guard );
		++m_pimpl->m_finished;
		if ( m_pimpl->m_finished >= m_pimpl->m_completion )
		{
			m_pimpl->m_condition.notify_all();
		}
	}
}

}
